/************************************************************************/
/*Copyright (c) James Zhao 2007,2008
/*This file is part of AdminOverlay.
/*AdminOverlay is free software: you can redistribute it and/or modify
/*it under the terms of the GNU General Public License as published by
/*the Free Software Foundation, either version 3 of the License, or
/*(at your option) any later version.
/*
/*AdminOverlay is distributed in the hope that it will be useful,
/*but WITHOUT ANY WARRANTY; without even the implied warranty of
/*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
/*GNU General Public License for more details.
/*
/*You should have received a copy of the GNU General Public License
/*along with AdminOverlay.  If not, see <http://www.gnu.org/licenses/>.                                                                     */
/*************************************************************************/
#include "GraphicsAdapter.h"
#include "detours.h"

GraphicsAdapter *GraphicsAdapter::currentInstance = NULL;

HRESULT (__stdcall *GraphicsAdapter::endSceneTrampoline)(LPDIRECT3DDEVICE9) = NULL;
HRESULT (__stdcall *GraphicsAdapter::resetTrampoline)(LPDIRECT3DDEVICE9, D3DPRESENT_PARAMETERS*) = NULL;

#define ENDSCENE_OFFSET_WINXP 553376
#define ENDSCENE_OFFSET_WINVISTA 14823

#define RESET_OFFSET_WINXP 276144
#define RESET_OFFSET_WINVISTA 834309


bool isResetted;
GraphicsAdapter::GraphicsAdapter(void)
{
	currentInstance = this;	
	isDeviceLost = false;
	isDeviceNull = true;
	isResetted = false;
}

GraphicsAdapter::~GraphicsAdapter(void)
{
}

void GraphicsAdapter::initalize(LPDIRECT3DDEVICE9 device)
{

}
bool GraphicsAdapter::isValid()
{
	return (!isDeviceNull) && (!isDeviceLost);
}

HRESULT __stdcall GraphicsAdapter::My_Reset( LPDIRECT3DDEVICE9 device , D3DPRESENT_PARAMETERS* pp )
{
	GraphicsAdapter::currentInstance->preResetHandler();
	HRESULT hr = GraphicsAdapter::resetTrampoline(device, pp);
	GraphicsAdapter::currentInstance->postResetHandler();
	return hr;
}
#include "CEGUIDefaultResourceProvider.h"
HRESULT __stdcall GraphicsAdapter::My_EndScene(LPDIRECT3DDEVICE9 device)
{
	if(currentInstance->disableForward)
	{		
		return endSceneTrampoline(device);		
	}
	if(!IsBadReadPtr(device, 1))
	{
		currentInstance->isDeviceNull = false;
		HRESULT cooperativeLevel = device->TestCooperativeLevel();
		if(cooperativeLevel == D3D_OK)
		{
			device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
			device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
			device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

			D3DVIEWPORT9 viewport;
			device->GetViewport(&viewport);
			currentInstance->cachedWidth = viewport.Width;
			currentInstance->cachedHeight = viewport.Height;

			IDirect3DBaseTexture9 *texture = NULL;
			IDirect3DVertexShader9 *vertexShader = NULL;
			IDirect3DPixelShader9 *pixelShader = NULL;

			device->GetTexture(0, &texture);
			device->GetVertexShader(&vertexShader);
			device->GetPixelShader(&pixelShader);

			device->SetTexture(0, NULL);
			device->SetVertexShader(NULL);
			device->SetPixelShader(NULL);


			currentInstance->isDeviceLost = false;
			currentInstance->draw(device);
			if(!IsBadReadPtr(texture,1) && texture != NULL)
			{
				device->SetTexture(0, texture);
			}
			if(!IsBadReadPtr(vertexShader,1) && vertexShader != NULL)
			{
				device->SetVertexShader(vertexShader);
			}
			if(!IsBadReadPtr(pixelShader,1) && pixelShader != NULL)
			{
				device->SetPixelShader(pixelShader);
			}
		}
		return GraphicsAdapter::endSceneTrampoline(device);
	}
	else
	{
		//the device can't be accessed
		currentInstance->isDeviceNull = true;
		return -1;
	}
}
void GraphicsAdapter::draw(LPDIRECT3DDEVICE9 device)
{	
	if(drawHandler != NULL)
	{
		drawHandler(device);
		return;
	}
}

void GraphicsAdapter::setupEndSceneDetour( int offset, LPCTSTR moduleName )
{
	PBYTE Real_EndScene = (PBYTE)reinterpret_cast<void*>(
		reinterpret_cast<ptrdiff_t>(GetModuleHandle(moduleName)) + offset);
	//make sure the real endscene is there
	if(!IsBadReadPtr(Real_EndScene, 1))
	{
		GraphicsAdapter::endSceneTrampoline = (HRESULT (__stdcall *)(LPDIRECT3DDEVICE9))
			DetourFunction((PBYTE)Real_EndScene, (PBYTE)GraphicsAdapter::My_EndScene);
	}
}


void GraphicsAdapter::setupResetDetour( int offset, LPCTSTR moduleName )
{
	PBYTE Real_Reset = (PBYTE)reinterpret_cast<void*>(
		reinterpret_cast<ptrdiff_t>(GetModuleHandle(moduleName)) + offset);
	//make sure the real endscene is there
	if(!IsBadReadPtr(Real_Reset, 1))
	{
		GraphicsAdapter::resetTrampoline = 
			(HRESULT (__stdcall *)(LPDIRECT3DDEVICE9, D3DPRESENT_PARAMETERS*))
			DetourFunction((PBYTE)Real_Reset, (PBYTE)GraphicsAdapter::My_Reset);
	}

}

void GraphicsAdapter::setupDetours()
{

	LoadLibrary(TEXT("d3d9.dll"));
	if(GetModuleHandle(TEXT("d3d9.dll")) != NULL)
	{	
		OSVERSIONINFO version_info;
		version_info.dwOSVersionInfoSize = sizeof(version_info);
		GetVersionEx(&version_info);

		if (version_info.dwMajorVersion == 5) 
		{
			setupEndSceneDetour(ENDSCENE_OFFSET_WINXP, TEXT("d3d9.dll"));
			setupResetDetour(RESET_OFFSET_WINXP, TEXT("d3d9.dll"));
		} 
		else if (version_info.dwMajorVersion == 6) 
		{
			setupEndSceneDetour(ENDSCENE_OFFSET_WINVISTA, TEXT("d3d9.dll"));
			setupResetDetour(RESET_OFFSET_WINVISTA, TEXT("d3d9.dll"));
		}
	}
}

void GraphicsAdapter::SetDrawHandler( void (*drawCallback)(LPDIRECT3DDEVICE9) )
{
	drawHandler = drawCallback;
}

void GraphicsAdapter::SetPreResetHandler( void (*resetHandler)() )
{
	preResetHandler = resetHandler;
}

void GraphicsAdapter::SetPostResetHandler( void (*resetHandler)() )
{
	postResetHandler = resetHandler;
}